/**
 * 5.接口
 * 接口可以在面向对象编程中表示行为的抽象，也可以描述对象的形状。
 * 接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
 * （接口中不能含有具体的实现逻辑）
 */
/**
 * 一、函数接口参数
 */
const fullName = ({firstName, lastName}: { firstName: string, lastName: string }): string => {
    return firstName + lastName;
}
// 我们可以约束函数中的参数，但是类型无法复用
interface IFullName {
    firstName: string,
    lastName: string
}
const fullName1 = ({ firstName, lastName }: IFullName): string => {
    return firstName + lastName;
}
// 我们可以通过接口进行描述

/**
 * 二、函数类型接口
 */
interface IFn {
    (obj: IFullName): string
}
const fullName2: IFn = ({ firstName, lastName }) => {
    return firstName + lastName;
}
// 通过接口限制函数的参数类型和返回值类型

/**
 * 三、函数混合类型
 */
// interface ICounter {
//     (): number; // 限制函数类型
//     count: 0 // 限制函数上的属性
// }
// let fn: any = () => {
//     fn.count++;
//     return fn.count;
// }
// fn.count = 0;
// let counter: ICounter = fn;
// console.log(counter());
// console.log(counter());

/**
 * 四、对象接口
 * 对象接口可以用来描述对象的形状结构
 */
interface IVegeTables {
    readonly color: string,
    size: string
}
interface IVegeTables {
    age?: number,
    taste: "sour" | "sweet"
}
const tomato: IVegeTables = {
    color: "red",
    size: "10",
    taste: "sour"
}
// tomato.color = "green"; // 仅读属性不能进行修改
// ? 标识的属性为可选属性，readOnly 标识的属性则不能修改。多个同名的接口会自动合并
const tomato1: IVegeTables = {
    color: "red",
    size: "10",
    taste: "sour",
    type: "蔬菜"
} as IVegeTables; // 多余的属性可以使用类型断言

/**
 * 五、任意属性、可索引接口
 */
interface PersonType {
    name: string,
    [key: string]: any
}
let p2: PersonType = {
    name: "dada",
    age: 10,
    [Symbol()]: "测试"
}
// 任意属性可以对某一部分必填属性做限制，其余的可以随意增减
interface IArr {
    [key: number]: any
}
let p23: IArr = {
    0: "1",
    1: "2",
    2: "3"
}
let ar2r: IArr = [1, "d", "c"];
// 可索引接口可以用于标识数组

/**
 * 六、类接口
 * 这里先来强调一下抽象类和接口的区别，
 * 抽象类中可以包含具体方法实现。
 * 接口中不能包含实现
 */
interface Speakable {
    name: string;
    speak(): void;
}
interface ChineseSpeakable {
    speakChinese(): void;
}
class Speak implements Speakable, ChineseSpeakable {
    name!: string;
    speak() {}
    speakChinese() {}
}
// 一个类可以实现多个接口，在类中必须实现接口中的方法和属性

/**
 * 七、接口继承
 */
interface Speakable1 {
    speak(): void;
}
interface SpeakChinese1 extends Speakable1 {
    speakChinese(): void;
}
class Speak1 implements SpeakChinese1 {
    speak(): void {
        throw new Error("Method not implemented.")
    }
    speakChinese(): void {
        throw new Error("Method not implemented.")
    }
}

/**
 * 八、构造函数类型
 */
interface Clazz {
    new (name: string): any;
}
function createClass(target: Clazz, name: string) {
    return new target(name); // 传入的是一个构造函数
}
class Animal45 {
    constructor(public name: string) {
        this.name = name;
    }
}
let r = createClass(Animal45, "Tom");
// 这里无法标识返回值类型

interface Clazz2<T> {
    new (name: string): T;
}
function createClass2<T>(target: Clazz2<T>, name: string):T {
    return new target(name);
}
class Animal24 {
    constructor(public name: string) {
        this.name = name;
    }
}
let r2 = createClass2(Animal24, "Tom");
// new() 表示当前是一个构造函数类型，这里捎带使用了下泛型。在使用 createClass 时动态传入类型。
